home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 3 / Amiga Format CD03 (1996-07-04)(Future Publishing)(GB)(Track 1 of 6)[!][issue 1996-08].iso / comms / netsoftware / archie38_1.lha / archie-1.4 / regex.c < prev    next >
C/C++ Source or Header  |  1995-01-05  |  16KB  |  708 lines

  1. #include "pmachine.h"
  2. /* Amiga port by Tomas Willis (tomas@cae.wisc.edu) January 1995 */
  3.  
  4. #ifdef NOREGEX
  5. /*
  6.  * These routines are BSD regex(3)/ed(1) compatible regular-expression
  7.  * routines written by Ozan S. Yigit, Computer Science, York University.
  8.  * Parts of the code that are not needed by Prospero have been removed,
  9.  * but most of the accompanying information has been left intact. 
  10.  * This file is to be included on those operating systems that do not
  11.  * support re_comp and re_exec.
  12.  */
  13.  
  14. /*
  15.  * regex - Regular expression pattern matching
  16.  *         and replacement
  17.  *
  18.  * by:  Ozan S. Yigit (oz@nexus.yorku.ca)
  19.  *    Dept. of Computing Services
  20.  *      York University
  21.  *
  22.  * These routines are the PUBLIC DOMAIN equivalents 
  23.  * of regex routines as found in 4.nBSD UN*X, with minor
  24.  * extensions.
  25.  *
  26.  * Modification history:
  27.  *
  28.  * $Log: regex.c,v $
  29.  * Revision 1.1  1991/11/20  02:32:13  brendan
  30.  * entered into RCS
  31.  *
  32.  * Revision 1.1  1991/11/20  02:32:13  brendan
  33.  * entered into RCS
  34.  *
  35.  * Revision 1.3  89/04/01  14:18:09  oz
  36.  * Change all references to a dfa: this is actually an nfa.
  37.  * 
  38.  * Revision 1.2  88/08/28  15:36:04  oz
  39.  * Use a complement bitmap to represent NCL.
  40.  * This removes the need to have seperate 
  41.  * code in the pmatch case block - it is 
  42.  * just CCL code now.
  43.  * 
  44.  * Use the actual CCL code in the CLO
  45.  * section of pmatch. No need for a recursive
  46.  * pmatch call.
  47.  * 
  48.  * Use a bitmap table to set char bits in an
  49.  * 8-bit chunk.
  50.  * 
  51.  * Routines:
  52.  *      re_comp:        compile a regular expression into
  53.  *                      a NFA.
  54.  *
  55.  *            char *re_comp(s)
  56.  *            char *s;
  57.  *
  58.  *      re_exec:        execute the NFA to match a pattern.
  59.  *
  60.  *            int re_exec(s)
  61.  *            char *s;
  62.  *
  63.  * Regular Expressions:
  64.  *
  65.  *      [1]     char    matches itself, unless it is a special
  66.  *                      character (metachar): . \ [ ] * + ^ $
  67.  *
  68.  *      [2]     .       matches any character.
  69.  *
  70.  *      [3]     \       matches the character following it, except
  71.  *            when followed by a left or right round bracket,
  72.  *            a digit 1 to 9 or a left or right angle bracket. 
  73.  *            (see [7], [8] and [9])
  74.  *            It is used as an escape character for all 
  75.  *            other meta-characters, and itself. When used
  76.  *            in a set ([4]), it is treated as an ordinary
  77.  *            character.
  78.  *
  79.  *      [4]     [set]   matches one of the characters in the set.
  80.  *                      If the first character in the set is "^",
  81.  *                      it matches a character NOT in the set, i.e. 
  82.  *            complements the set. A shorthand S-E is 
  83.  *            used to specify a set of characters S upto 
  84.  *            E, inclusive. The special characters "]" and 
  85.  *            "-" have no special meaning if they appear 
  86.  *            as the first chars in the set.
  87.  *                      examples:        match:
  88.  *
  89.  *                              [a-z]    any lowercase alpha
  90.  *
  91.  *                              [^]-]    any char except ] and -
  92.  *
  93.  *                              [^A-Z]   any char except uppercase
  94.  *                                       alpha
  95.  *
  96.  *                              [a-zA-Z] any alpha
  97.  *
  98.  *      [5]     *       any regular expression form [1] to [4], followed by
  99.  *                      closure char (*) matches zero or more matches of
  100.  *                      that form.
  101.  *
  102.  *      [6]     +       same as [5], except it matches one or more.
  103.  *
  104.  *      [7]             a regular expression in the form [1] to [10], enclosed
  105.  *                      as \(form\) matches what form matches. The enclosure
  106.  *                      creates a set of tags, used for [8] and for
  107.  *                      pattern substution. The tagged forms are numbered
  108.  *            starting from 1.
  109.  *
  110.  *      [8]             a \ followed by a digit 1 to 9 matches whatever a
  111.  *                      previously tagged regular expression ([7]) matched.
  112.  *
  113.  *    [9]    \<    a regular expression starting with a \< construct
  114.  *        \>    and/or ending with a \> construct, restricts the
  115.  *            pattern matching to the beginning of a word, and/or
  116.  *            the end of a word. A word is defined to be a character
  117.  *            string beginning and/or ending with the characters
  118.  *            A-Z a-z 0-9 and _. It must also be preceded and/or
  119.  *            followed by any character outside those mentioned.
  120.  *
  121.  *      [10]            a composite regular expression xy where x and y
  122.  *                      are in the form [1] to [10] matches the longest
  123.  *                      match of x followed by a match for y.
  124.  *
  125.  *      [11]    ^    a regular expression starting with a ^ character
  126.  *        $    and/or ending with a $ character, restricts the
  127.  *                      pattern matching to the beginning of the line,
  128.  *                      or the end of line. [anchors] Elsewhere in the
  129.  *            pattern, ^ and $ are treated as ordinary characters.
  130.  *
  131.  *
  132.  * Acknowledgements:
  133.  *
  134.  *    HCR's Hugh Redelmeier has been most helpful in various
  135.  *    stages of development. He convinced me to include BOW
  136.  *    and EOW constructs, originally invented by Rob Pike at
  137.  *    the University of Toronto.
  138.  *
  139.  * References:
  140.  *              Software tools            Kernighan & Plauger
  141.  *              Software tools in Pascal        Kernighan & Plauger
  142.  *              Grep [rsx-11 C dist]            David Conroy
  143.  *        ed - text editor        Un*x Programmer's Manual
  144.  *        Advanced editing on Un*x    B. W. Kernighan
  145.  *        regexp routines            Henry Spencer
  146.  *
  147.  * Notes:
  148.  *
  149.  *    This implementation uses a bit-set representation for character
  150.  *    classes for speed and compactness. Each character is represented 
  151.  *    by one bit in a 128-bit block. Thus, CCL always takes a 
  152.  *    constant 16 bytes in the internal nfa, and re_exec does a single
  153.  *    bit comparison to locate the character in the set.
  154.  *
  155.  * Examples:
  156.  *
  157.  *    pattern:    foo*.*
  158.  *    compile:    CHR f CHR o CLO CHR o END CLO ANY END END
  159.  *    matches:    fo foo fooo foobar fobar foxx ...
  160.  *
  161.  *    pattern:    fo[ob]a[rz]    
  162.  *    compile:    CHR f CHR o CCL bitset CHR a CCL bitset END
  163.  *    matches:    fobar fooar fobaz fooaz
  164.  *
  165.  *    pattern:    foo\\+
  166.  *    compile:    CHR f CHR o CHR o CHR \ CLO CHR \ END END
  167.  *    matches:    foo\ foo\\ foo\\\  ...
  168.  *
  169.  *    pattern:    \(foo\)[1-3]\1    (same as foo[1-3]foo)
  170.  *    compile:    BOT 1 CHR f CHR o CHR o EOT 1 CCL bitset REF 1 END
  171.  *    matches:    foo1foo foo2foo foo3foo
  172.  *
  173.  *    pattern:    \(fo.*\)-\1
  174.  *    compile:    BOT 1 CHR f CHR o CLO ANY END EOT 1 CHR - REF 1 END
  175.  *    matches:    foo-foo fo-fo fob-fob foobar-foobar ...
  176.  * 
  177.  */
  178.  
  179. #define MAXNFA  1024
  180. #define MAXTAG  10
  181.  
  182. #define OKP     1
  183. #define NOP     0
  184.  
  185. #define CHR     1
  186. #define ANY     2
  187. #define CCL     3
  188. #define BOL     4
  189. #define EOL     5
  190. #define BOT     6
  191. #define EOT     7
  192. #define BOW    8
  193. #define EOW    9
  194. #define REF     10
  195. #define CLO     11
  196.  
  197. #define END     0
  198.  
  199. /*
  200.  * The following defines are not meant
  201.  * to be changeable. They are for readability
  202.  * only.
  203.  *
  204.  */
  205. #define MAXCHR    128
  206. #define CHRBIT    8
  207. #define BITBLK    MAXCHR/CHRBIT
  208. #define BLKIND    0170
  209. #define BITIND    07
  210.  
  211. #define ASCIIB    0177
  212.  
  213. typedef /*unsigned*/ char CHAR;
  214.  
  215. static int  tagstk[MAXTAG];             /* subpat tag stack..*/
  216. static CHAR nfa[MAXNFA];        /* automaton..       */
  217. static int  sta = NOP;                   /* status of lastpat */
  218.  
  219. static CHAR bittab[BITBLK];        /* bit table for CCL */
  220.                     /* pre-set bits...   */
  221. static CHAR bitarr[] = {1,2,4,8,16,32,64,128};
  222.  
  223. static int internal_error;
  224.  
  225. //protos
  226. static void chset(register CHAR c);
  227. char * re_comp(char *pat);
  228. int re_exec(char *lp);
  229. static char * pmatch(char *lp, CHAR *ap);
  230.  
  231. //extern
  232.  
  233.  
  234. static void
  235. chset(register CHAR c)
  236. //register CHAR c;
  237. {
  238.     bittab[((c) & BLKIND) >> 3] |= bitarr[(c) & BITIND];
  239. }
  240.  
  241. #define badpat(x)    return (*nfa = END, x)
  242. #define store(x)    *mp++ = x
  243.  
  244. char *
  245. re_comp(char *pat)
  246. //char *pat;
  247. {
  248.     register char *p;               /* pattern pointer   */
  249.     register CHAR *mp = nfa;        /* nfa pointer       */
  250.     register CHAR *lp;              /* saved pointer..   */
  251.     register CHAR *sp = nfa;        /* another one..     */
  252.  
  253.     register int tagi = 0;          /* tag stack index   */
  254.     register int tagc = 1;          /* actual tag count  */
  255.  
  256.     register int n;
  257.     register CHAR mask;        /* xor mask -CCL/NCL */
  258.     int c1, c2;
  259.         
  260.     if (!pat || !*pat)
  261.         if (sta)
  262.             return 0;
  263.         else
  264.             badpat("No previous regular expression");
  265.     sta = NOP;
  266.  
  267.     for (p = pat; *p; p++) {
  268.         lp = mp;
  269.         switch(*p) {
  270.  
  271.         case '.':               /* match any char..  */
  272.             store(ANY);
  273.             break;
  274.  
  275.         case '^':               /* match beginning.. */
  276.             if (p == pat)
  277.                 store(BOL);
  278.             else {
  279.                 store(CHR);
  280.                 store(*p);
  281.             }
  282.             break;
  283.  
  284.         case '$':               /* match endofline.. */
  285.             if (!*(p+1))
  286.                 store(EOL);
  287.             else {
  288.                 store(CHR);
  289.                 store(*p);
  290.             }
  291.             break;
  292.  
  293.         case '[':               /* match char class..*/
  294.             store(CCL);
  295.  
  296.             if (*++p == '^') {
  297.                 mask = 0377;    
  298.                 p++;
  299.             }
  300.             else
  301.                 mask = 0;
  302.  
  303.             if (*p == '-')        /* real dash */
  304.                 chset(*p++);
  305.             if (*p == ']')        /* real brac */
  306.                 chset(*p++);
  307.             while (*p && *p != ']') {
  308.                 if (*p == '-' && *(p+1) && *(p+1) != ']') {
  309.                     p++;
  310.                     c1 = *(p-2) + 1;
  311.                     c2 = *p++;
  312.                     while (c1 <= c2)
  313.                         chset(c1++);
  314.                 }
  315. #ifdef EXTEND
  316.                 else if (*p == '\\' && *(p+1)) {
  317.                     p++;
  318.                     chset(*p++);
  319.                 }
  320. #endif
  321.                 else
  322.                     chset(*p++);
  323.             }
  324.             if (!*p)
  325.                 badpat("Missing ]");
  326.  
  327.             for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
  328.                 store(mask ^ bittab[n]);
  329.     
  330.             break;
  331.  
  332.         case '*':               /* match 0 or more.. */
  333.         case '+':               /* match 1 or more.. */
  334.             if (p == pat)
  335.                 badpat("Empty closure");
  336.             lp = sp;        /* previous opcode */
  337.             if (*lp == CLO)        /* equivalence..   */
  338.                 break;
  339.             switch(*lp) {
  340.  
  341.             case BOL:
  342.             case BOT:
  343.             case EOT:
  344.             case BOW:
  345.             case EOW:
  346.             case REF:
  347.                 badpat("Illegal closure");
  348.             default:
  349.                 break;
  350.             }
  351.  
  352.             if (*p == '+')
  353.                 for (sp = mp; lp < sp; lp++)
  354.                     store(*lp);
  355.  
  356.             store(END);
  357.             store(END);
  358.             sp = mp;
  359.             while (--mp > lp)
  360.                 *mp = mp[-1];
  361.             store(CLO);
  362.             mp = sp;
  363.             break;
  364.  
  365.         case '\\':              /* tags, backrefs .. */
  366.             switch(*++p) {
  367.  
  368.             case '(':
  369.                 if (tagc < MAXTAG) {
  370.                     tagstk[++tagi] = tagc;
  371.                     store(BOT);
  372.                     store(tagc++);
  373.                 }
  374.                 else
  375.                     badpat("Too many \\(\\) pairs");
  376.                 break;
  377.             case ')':
  378.                 if (*sp == BOT)
  379.                     badpat("Null pattern inside \\(\\)");
  380.                 if (tagi > 0) {
  381.                     store(EOT);
  382.                     store(tagstk[tagi--]);
  383.                 }
  384.                 else
  385.                     badpat("Unmatched \\)");
  386.                 break;
  387.             case '<':
  388.                 store(BOW);
  389.                 break;
  390.             case '>':
  391.                 if (*sp == BOW)
  392.                     badpat("Null pattern inside \\<\\>");
  393.                 store(EOW);
  394.                 break;
  395.             case '1':
  396.             case '2':
  397.             case '3':
  398.             case '4':
  399.             case '5':
  400.             case '6':
  401.             case '7':
  402.             case '8':
  403.             case '9':
  404.                 n = *p-'0';
  405.                 if (tagi > 0 && tagstk[tagi] == n)
  406.                     badpat("Cyclical reference");
  407.                 if (tagc > n) {
  408.                     store(REF);
  409.                     store(n);
  410.                 }
  411.                 else
  412.                     badpat("Undetermined reference");
  413.                 break;
  414. #ifdef EXTEND
  415.             case 'b':
  416.                 store(CHR);
  417.                 store('\b');
  418.                 break;
  419.             case 'n':
  420.                 store(CHR);
  421.                 store('\n');
  422.                 break;
  423.             case 'f':
  424.                 store(CHR);
  425.                 store('\f');
  426.                 break;
  427.             case 'r':
  428.                 store(CHR);
  429.                 store('\r');
  430.                 break;
  431.             case 't':
  432.                 store(CHR);
  433.                 store('\t');
  434.                 break;
  435. #endif
  436.             default:
  437.                 store(CHR);
  438.                 store(*p);
  439.             }
  440.             break;
  441.  
  442.         default :               /* an ordinary char  */
  443.             store(CHR);
  444.             store(*p);
  445.             break;
  446.         }
  447.         sp = lp;
  448.     }
  449.     if (tagi > 0)
  450.         badpat("Unmatched \\(");
  451.     store(END);
  452.     sta = OKP;
  453.     return 0;
  454. }
  455.  
  456.  
  457. static char *bol;
  458. static char *bopat[MAXTAG];
  459. static char *eopat[MAXTAG];
  460. //char *pmatch();
  461.  
  462. /*
  463.  * re_exec:
  464.  *     execute nfa to find a match.
  465.  *
  466.  *    special cases: (nfa[0])
  467.  *        BOL
  468.  *            Match only once, starting from the
  469.  *            beginning.
  470.  *        CHR
  471.  *            First locate the character without
  472.  *            calling pmatch, and if found, call
  473.  *            pmatch for the remaining string.
  474.  *        END
  475.  *            re_comp failed, poor luser did not
  476.  *            check for it. Fail fast.
  477.  *
  478.  *    If a match is found, bopat[0] and eopat[0] are set
  479.  *    to the beginning and the end of the matched fragment,
  480.  *    respectively.
  481.  *
  482.  */
  483.  
  484. int
  485. re_exec(register char *lp)
  486. //register char *lp;
  487. {
  488.     register char c;
  489.     register char *ep = 0;
  490.     register CHAR *ap = nfa;
  491.  
  492.     bol = lp;
  493.  
  494.     bopat[0] = 0;
  495.     bopat[1] = 0;
  496.     bopat[2] = 0;
  497.     bopat[3] = 0;
  498.     bopat[4] = 0;
  499.     bopat[5] = 0;
  500.     bopat[6] = 0;
  501.     bopat[7] = 0;
  502.     bopat[8] = 0;
  503.     bopat[9] = 0;
  504.  
  505.     switch(*ap) {
  506.  
  507.     case BOL:            /* anchored: match from BOL only */
  508.         ep = pmatch(lp,ap);
  509.         break;
  510.     case CHR:            /* ordinary char: locate it fast */
  511.         c = *(ap+1);
  512.         while (*lp && *lp != c)
  513.             lp++;
  514.         if (!*lp)        /* if EOS, fail, else fall thru. */
  515.             return 0;
  516.     default:            /* regular matching all the way. */
  517.         while (*lp) {
  518.             if ((ep = pmatch(lp,ap)))
  519.                 break;
  520.             lp++;
  521.         }
  522.         break;
  523.     case END:            /* munged automaton. fail always */
  524.         return 0;
  525.     }
  526.     if (!ep)
  527.         return 0;
  528.  
  529.     if (internal_error)
  530.         return -1;
  531.  
  532.     bopat[0] = lp;
  533.     eopat[0] = ep;
  534.     return 1;
  535. }
  536.  
  537. /*
  538.  * pmatch:
  539.  *    internal routine for the hard part
  540.  *
  541.  *     This code is mostly snarfed from an early
  542.  *     grep written by David Conroy. The backref and
  543.  *     tag stuff, and various other mods are by oZ.
  544.  *
  545.  *    special cases: (nfa[n], nfa[n+1])
  546.  *        CLO ANY
  547.  *            We KNOW ".*" will match ANYTHING
  548.  *            upto the end of line. Thus, go to
  549.  *            the end of line straight, without
  550.  *            calling pmatch recursively. As in
  551.  *            the other closure cases, the remaining
  552.  *            pattern must be matched by moving
  553.  *            backwards on the string recursively,
  554.  *            to find a match for xy (x is ".*" and
  555.  *            y is the remaining pattern) where
  556.  *            the match satisfies the LONGEST match
  557.  *            for x followed by a match for y.
  558.  *        CLO CHR
  559.  *            We can again scan the string forward
  560.  *            for the single char without recursion,
  561.  *            and at the point of failure, we execute
  562.  *            the remaining nfa recursively, as
  563.  *            described above.
  564.  *
  565.  *    At the end of a successful match, bopat[n] and eopat[n]
  566.  *    are set to the beginning and end of subpatterns matched
  567.  *    by tagged expressions (n = 1 to 9).
  568.  *
  569.  */
  570.  
  571. /*
  572.  * character classification table for word boundary
  573.  * operators BOW and EOW. the reason for not using
  574.  * ctype macros is that we can let the user add into
  575.  * our own table. see re_modw. This table is not in
  576.  * the bitset form, since we may wish to extend it
  577.  * in the future for other character classifications.
  578.  *
  579.  *    TRUE for 0-9 A-Z a-z _
  580.  */
  581. static char chrtyp[MAXCHR] = {
  582.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  583.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  584.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  585.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  586.     0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
  587.     1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  588.     0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
  589.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  590.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  591.     1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
  592.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  593.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  594.     1, 1, 1, 0, 0, 0, 0, 0
  595.     };
  596.  
  597. #define inascii(x)    (0177&(x))
  598. #define iswordc(x)     chrtyp[inascii(x)]
  599. #define isinset(x,y)     ((x)[((y)&BLKIND)>>3] & bitarr[(y)&BITIND])
  600.  
  601. /*
  602.  * skip values for CLO XXX to skip past the closure
  603.  *
  604.  */
  605.  
  606. #define ANYSKIP    2     /* [CLO] ANY END ...         */
  607. #define CHRSKIP    3    /* [CLO] CHR chr END ...     */
  608. #define CCLSKIP 18    /* [CLO] CCL 16bytes END ... */
  609.  
  610. static char *
  611. pmatch(register char *lp, register CHAR *ap)
  612. //register char *lp;
  613. //register CHAR *ap;
  614. {
  615.     register int op, c, n;
  616.     register char *e;        /* extra pointer for CLO */
  617.     register char *bp;        /* beginning of subpat.. */
  618.     register char *ep;        /* ending of subpat..     */
  619.     char *are;            /* to save the line ptr. */
  620.  
  621.     while ((op = *ap++) != END)
  622.         switch(op) {
  623.  
  624.         case CHR:
  625.             if (*lp++ != *ap++)
  626.                 return 0;
  627.             break;
  628.         case ANY:
  629.             if (!*lp++)
  630.                 return 0;
  631.             break;
  632.         case CCL:
  633.             c = *lp++;
  634.             if (!isinset(ap,c))
  635.                 return 0;
  636.             ap += BITBLK;
  637.             break;
  638.         case BOL:
  639.             if (lp != bol)
  640.                 return 0;
  641.             break;
  642.         case EOL:
  643.             if (*lp)
  644.                 return 0;
  645.             break;
  646.         case BOT:
  647.             bopat[*ap++] = lp;
  648.             break;
  649.         case EOT:
  650.             eopat[*ap++] = lp;
  651.             break;
  652.          case BOW:
  653.             if (lp!=bol && iswordc(lp[-1]) || !iswordc(*lp))
  654.                 return 0;
  655.             break;
  656.         case EOW:
  657.             if (lp==bol || !iswordc(lp[-1]) || iswordc(*lp))
  658.                 return 0;
  659.             break;
  660.         case REF:
  661.             n = *ap++;
  662.             bp = bopat[n];
  663.             ep = eopat[n];
  664.             while (bp < ep)
  665.                 if (*bp++ != *lp++)
  666.                     return 0;
  667.             break;
  668.         case CLO:
  669.             are = lp;
  670.             switch(*ap) {
  671.  
  672.             case ANY:
  673.                 while (*lp)
  674.                     lp++;
  675.                 n = ANYSKIP;
  676.                 break;
  677.             case CHR:
  678.                 c = *(ap+1);
  679.                 while (*lp && c == *lp)
  680.                     lp++;
  681.                 n = CHRSKIP;
  682.                 break;
  683.             case CCL:
  684.                 while ((c = *lp) && isinset(ap+1,c))
  685.                     lp++;
  686.                 n = CCLSKIP;
  687.                 break;
  688.             default:
  689.                 internal_error++;
  690.                 return 0;
  691.             }
  692.  
  693.             ap += n;
  694.  
  695.             while (lp >= are) {
  696.                 if (e = pmatch(lp, ap))
  697.                     return e;
  698.                 --lp;
  699.             }
  700.             return 0;
  701.         default:
  702.             internal_error++;
  703.             return 0;
  704.         }
  705.     return lp;
  706. }
  707. #endif /* Need regex libraries? Compile to nothing if not.  */
  708.